Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at JavaScript symbols.
Crossing Realms with Symbols
Symbols don’t travel well across realms.
Since there’re different global objects in different frames, we can’t use them across frames.
Special symbols like Symbol.iterator
should work across different realms.
To get a symbol across different realms, we can use the Symbol.for
method to get the symbol with a string.
For example, we can write:
const sym = Symbol.for('foo');
Then we can call Symbol.keyFor
to get the string we passed into Symbol
to create the symbol:
Symbol.keyFor(sym)
Special symbols like Symbol.iterator
will return undefined
if we call it with Symbol.keyFor
:
Symbol.keyFor(Symbol.iterator)
Are Symbols Primitives or Objects?
Symbols are primitive values.
Symbols are like strings in that they can be used as property keys.
But they’re like objects in that each symbol has their own identity.
They’re immutable and they can be used as property keys.
However, it doesn’t have many properties of objects like prototypes and wrappers.
Symbol also can’t be examined by operators and methods like instance
or Object.keys
.
Why are Symbols Useful?
Symbols are useful for avoid clashes of identifiers.
This is easy since we can’t create the same symbol twice.
Are JavaScript Symbols Like Ruby’s Symbols?
JavaScript and Ruby symbols aren’t alike.
Ruby symbols are literals for creating values.
For instance, if we have:
`:foo` `==` `:foo`
then the expression returns true
.
Symbol(‘foo’) !== Symbol(‘foo’)
returns true
.
So no 2 symbols are the same even though we passed in the same argument.
The Spelling of Well-Known Symbols
Well-known symbols are spelled this way because they’re used as normal property keys.
Symbol API
Symbol
is a function that takes a string as its description.
So we can use it by writing:
const sym = Symbol('foo');
to get a new symbol.
Methods of Symbols
The only useful method in a symbol is the toString
method.
Well-known Symbols
There’re several well-knowns that may be useful to us.
The Symbol.hasInstance
method lets an object customize the behavior of the instanceof
operator.
Symbol.toPrimitive
is a method that lets us customize how it’s converted to a primitive value.
This is the 1st steep whenever something is being coerced into a primitive value.
Symbol.toStringTag
is a method called by Object.prototype.toString
to return the string description of an object.
Therefore, we can override it to provide our own string description.
Symbol.unscopables
is a method that hides some properties with the with
statement.
Symbol.iterator
is a method that lets us define our own iterable to create an iterable object.
Once we added this method, we can iterate it with the for-of operator.
String Methods
String methods are forwarded to methods with the given symbols.
They’re forwarded as follows:
String.prototype.match(str, ...)
is forwarded tostr[Symbol.match]()
.String.prototype.replace(str, ...)
is forwarded tostr[Symbol.replace]()
.String.prototype.search(str, ...)
is forwarded tostr[Symbol.search](···)
.String.prototype.split(str, ...)
is forwarded tostr[Symbol.split]()
.
Other Special Symbols
Symbol.species
is a method to configure built-in methods to create objects similar to this
.
Symbol.isConcatSpreadable
is a boolean to configure whether Array.prototype.concat
adds the indexed element as the result.
Conclusion
There’re many special symbols we can use to override the behavior of objects.